package main

import (
	_ "TaskAssignmentSystem/internal/admin/cmd/docs"
	"TaskAssignmentSystem/internal/admin/dao"
	. "TaskAssignmentSystem/internal/admin/router"
	"TaskAssignmentSystem/internal/admin/services"
	"TaskAssignmentSystem/internal/pkg/task"
	Redis "TaskAssignmentSystem/pkg/db/redis"
	"TaskAssignmentSystem/pkg/timehandler"
	"TaskAssignmentSystem/pkg/utils"
	"log"
	"os/exec"
	"sync"

	adminserver "TaskAssignmentSystem/internal/admin/services"
	"fmt"
	"github.com/go-co-op/gocron"
	"github.com/go-redis/redis"
	"github.com/kataras/iris/v12"
	"github.com/robfig/cron"
	"strconv"
	"strings"
	"time"
)

//golang 定时器，启动的时候执行一次，以后每天早晨7点执行

func StartTimer() {
	c := cron.New()
	//每日凌晨12点执行
	c.AddFunc("0 0 0 * * ?", func() { SelectCron() })
	//c.AddFunc("0 0 1 1 * ?", func() { TeamReSetCron() })
	c.AddFunc("0 0 1 * * ?", func() { ClearCacheCron() })
	//c.AddFunc("@every 90d", func() { api.DoTask() })
	c.AddFunc("0 0/10 * * * ?", func() { TeamIncomeCron() })
	c.Start()
	select {}
}

// 10分钟计算一次团队收入
func TeamIncomeCron() {
	var teamServe adminserver.TeamService
	teamServe.TeamIncomeCron()
	fmt.Println("Cron TeamIncomeCron")
}

// 每月1号凌晨1点。团队数据重新调整
func TeamReSetCron() {
	var teamServe adminserver.TeamService
	teamServe.TeamInsertHistory()
	fmt.Println("Cron TeamReSetCron")

}

// 清理服务器缓存
func ClearCacheCron() {
	// 指定要执行的Shell脚本文件
	scriptPath := "/docker/go/empty_files.sh"

	// 创建一个exec.Cmd结构体来表示要执行的命令
	cmd := exec.Command("/bin/bash", scriptPath)

	// 执行命令并捕获标准输出和标准错误
	output, err := cmd.CombinedOutput()

	// 检查是否有错误发生
	if err != nil {
		log.Fatalf("Error executing script: %v\nOutput:\n%s", err, output)
	}

	// 打印脚本的输出
	log.Printf("Script executed successfully. Output:\n%s", output)
}

// 启动定时任务
// 0:题目答题倒计时 1:指派 2:回归题库 3:返修 4:作废 5:题目生效时间更改题目状态 6:题目截止时间过期更改题目状态
func SelectCron() {
	s := GetSchedulerCheckLockSingleton()
	s.Clear()
	go TaskListener(1)
	go TaskListener(2)
	go TaskListener(3)
	go TaskListener(4)
	go TaskListener(5)
	go TaskListener(6)
}

// @title TaskAssignmentSystem 后台系统接口
// @version 1.0.0

// @BasePath /api
func main() {
	//任务监听服务
	go Subscribe(Redis.RedisGClient)
	//定时任务处理
	go StartTimer()
	//广告开启和关闭
	go SubscribeAdv(Redis.RedisGClient)
	//启动程序执行一次
	SelectCron()

	address := iris.Addr(utils.ServiceString())
	yamlRes := iris.YAML(utils.IrisPath)
	yamlRes.Timeout = 60 * 300 * time.Second
	configuration := iris.WithConfiguration(yamlRes)
	App.Application.Run(address, configuration)
}

func Subscribe(r *redis.Client) {
	pubSub := r.PSubscribe(fmt.Sprintf("__keyevent@%d__:expired", utils.ServerConf.RedisConf.DatabaseName))
	_, err := pubSub.Receive()
	if err != nil {
		return
	}
	ch := pubSub.Channel()
	for msg := range ch {
		//添加异步处理
		go func(msg *redis.Message) {
			if strings.Contains(msg.Payload, "task_topic") {
				split := strings.Split(msg.Payload, "@")
				values := strings.Split(split[1], "_")
				topicId, _ := strconv.Atoi(values[0])
				/*
				 * 处理事件
				 * TaskType 类型: 0->题目答题倒计时,管理员导入表格处理数据时的任务;1->指派;2->回归题库;3->返修;4->作废;5->题目生效时间更改题目状态
				 * Topic状态 0未生效 1已完成 2已领取 3指派中 4返修中 5闲置中 6已作废 7已失效
				 * 返修截止时间，超过截止时间，自动回归题库
				 */
				var topicServe services.TopicService
				switch values[1] {
				case "2": //回归题库
					topicServe.RegressionQuestionBank(uint(topicId), time.Now().In(utils.LocationZone).Add(-1*time.Second))
				case "3": //返修
					topicServe.TopicRepair(uint(topicId), time.Now().In(utils.LocationZone).Add(-1*time.Second))
				case "4": //作废
					topicServe.InvalidTopic(uint(topicId), time.Now().In(utils.LocationZone).Add(-1*time.Second))
				case "1": //指派
					topicServe.TopicAssignUser(uint(topicId), values[2], time.Now().Add(-1*time.Second))
				case "0": //题目答题倒计时,管理员导入表格处理数据时的任务
					topicServe.TopicCountdownAnswer(uint(topicId), time.Now().In(utils.LocationZone).Add(-1*time.Second))
				case "5": //题目生效更改状态
					topicServe.RegressionQuestionBank(uint(topicId), time.Now().In(utils.LocationZone).Add(-1*time.Second))
				case "6": //题目失效更改状态
					topicServe.RegressionQuestionClose(uint(topicId), time.Now().In(utils.LocationZone).Add(-1*time.Second))
				default:
					//TODO
				}
			}
			if msg.Payload == "TopicAssignUser" {
				TaskListener(1)
				TaskListener(6)
			}
			if msg.Payload == "BatchRegressionQuestionBank" {
				TaskListener(2)
				TaskListener(6)
			}
			if msg.Payload == "TopicRepair" {
				TaskListener(3)
				TaskListener(6)
			}
			if msg.Payload == "InvalidTopic" {
				TaskListener(4)
				TaskListener(6)
			}
			if msg.Payload == "TaskTopicStart" {
				TaskListener(5)
				TaskListener(6)
			}
			if msg.Payload == "TaskTopicEnd" {
				TaskListener(6)
			}
			if strings.Contains(msg.Payload, "task_project") {
				split := strings.Split(msg.Payload, "@")
				values := strings.Split(split[1], "_")
				projectId, _ := strconv.Atoi(values[0])
				var status int
				switch values[1] {
				case "10":
					status = 1
				case "11":
					status = 2
				}

				var projectServe services.ProjectService
				projectServe.UpdateProjectStatus(uint(projectId), status)
			}
		}(msg)

	}
}

func SubscribeAdv(r *redis.Client) {
	pubSub := r.PSubscribe(fmt.Sprintf("__keyevent@%d__:expired", utils.ServerConf.RedisConf.DatabaseName))
	_, err := pubSub.Receive()
	if err != nil {
		return
	}
	ch := pubSub.Channel()
	for msg := range ch {
		//添加异步处理
		go func(msg *redis.Message) {
			var advDao dao.Common[dao.SystemAdv]
			if strings.Contains(msg.Payload, "adv_open") {
				split := strings.Split(msg.Payload, "@")
				advId := split[1]
				if advId, err := utils.StringToUint(advId); err == nil {
					advDao.UpdateOne(advId, map[string]interface{}{"status": 1})
				}
			} else if strings.Contains(msg.Payload, "adv_close") {
				split := strings.Split(msg.Payload, "@")
				advId := split[1]
				if advId, err := utils.StringToUint(advId); err == nil {
					advDao.UpdateOne(advId, map[string]interface{}{"status": 0})
				}
			}
		}(msg)

	}
}

var lock sync.Mutex
var cronScheduler *gocron.Scheduler

func GetSchedulerCheckLockSingleton() *gocron.Scheduler {
	if cronScheduler == nil {
		lock.Lock()
		defer lock.Unlock()
		if cronScheduler == nil {
			timezone, _ := time.LoadLocation("Asia/Shanghai")
			cronScheduler = gocron.NewScheduler(timezone)
		}
	}
	return cronScheduler
}

// TaskListener 定时任务
// 0:题目答题倒计时 1:指派 2:回归题库 3:返修 4:作废 5:题目生效时间更改题目状态 6:题目截止时间过期更改题目状态
func TaskListener(taskType int) {
	//timezone, _ := time.LoadLocation("Asia/Shanghai")
	//s := gocron.NewScheduler(timezone)
	s := GetSchedulerCheckLockSingleton()
	// 查询当天的任务计划
	var systemTaskServe services.CommonService[dao.SystemTask]
	getDayRange := timehandler.GetDayRange(time.Now())
	systemTaskServe.WhereStr = append(systemTaskServe.WhereStr, fmt.Sprintf("task_type = %d", taskType))
	systemTaskServe.WhereStr = append(systemTaskServe.WhereStr, fmt.Sprintf("execution_date_time BETWEEN '%s' AND '%s'", time.Now().In(utils.LocationZone).Format(timehandler.FormatLayoutTime), getDayRange.End.Format(timehandler.FormatLayoutTime)))

	caps := 5000
	for {
		systemTaskServe.Page += 1
		systemTaskServe.PageSize = int64(caps)
		res, _ := systemTaskServe.SelectList()
		if len(res.Data) == 0 {
			break
		}
		if taskType != 1 {
			var set = make(map[string][]uint, 0)
			for _, topic := range res.Data {
				set[topic.ExecutionDateTime.Format("15:04:05")] = append(set[topic.ExecutionDateTime.Format("15:04:05")], topic.SystemTopicId)
			}
			for k, v := range set {
				// 每天定时执行一次，包含今天
				s.Every(1).Day().At(k).Do(func(topicId []uint, taskType int) {
					go task.ToDoTask(topicId, taskType)
				}, v, taskType)
			}
		} else {
			var set = make(map[string][]dao.SystemTask, 0)
			for _, topic := range res.Data {
				set[topic.ExecutionDateTime.Format("15:04:05")] = append(set[topic.ExecutionDateTime.Format("15:04:05")], topic)
			}
			for k, v := range set {
				// 每天定时执行一次，包含今天
				s.Every(1).Day().At(k).Do(func(tasks []dao.SystemTask, taskType int) {
					go task.AssignUser(tasks)
				}, v, taskType)
			}
		}
	}
	s.StartAsync()

}
